#include "dataanalyzer.h"
#include "openfiledialog.h"
#include "crcconfigdlg.h"

#include <QFileDialog>
#include <QByteArray>
#include <QFile>
#include <QScrollBar>
#include <QCoreApplication>
#include <QPointer>

#include <assert.h>
#include <boost/crc.hpp>



#define INI_FILENAME					"DataAnalyzer.ini"
#define SETIINGS_CRC_GROUP				"CRC"
#define SETIINGS_CRC_NAME				"CRC_Name"
#define SETIINGS_CRC_COUNT				"crc_count"

#define SETIINGS_CRC_BITS				"crc_bits"
#define SETIINGS_CRC_POLYNOM			"truncated_polynominal"
#define SETIINGS_CRC_REMINDER			"initial_remainder"
#define SETIINGS_CRC_FINAL_XOR_VALUE	"final_xor_value"
#define SETIINGS_CRC_REFLECT_INPUT		"reflect_input"
#define SETIINGS_CRC_REFLECT_REMINDER	"reflect_remainder"
//#define SETIINGS_		""

#define SETTINGS_ITERATION_NAME(p, iii) QString("%1_%2").arg(p).arg(iii)


DataAnalyzer::DataAnalyzer(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
	, _model(this)
	, _byteDelegate(this)
{
	ui.setupUi(this);

	//_cmbViewMode = new QComboBox(this);
	//_cmbViewMode->addItem(tr("Decimal"));
	//_cmbViewMode->addItem(tr("HEX"));
	//_cmbViewMode->addItem(tr("Binary"));
	//_cmbViewMode->addItem(tr("BinaryInverted"));
	//_cmbViewMode->addItem(tr("Symbol"));
	//ui.mainToolBar->addWidget(_cmbViewMode);

	//connect(_cmbViewMode, SIGNAL(currentIndexChanged(int)), this, SLOT(on_comboboxViewMode_currentIndexChanged(int)));

	// Set StaticContents to enable minimal repaints on resizes.
	ui.tableViewData->viewport()->setAttribute(Qt::WA_StaticContents);
	ui.tableViewData->setItemDelegate(&_byteDelegate);
	ui.tableViewData->setModel(&_model);

	ui.toolButtonCRCConfig->setDefaultAction(ui.actionCRCconfig);

	QItemSelectionModel * sel = ui.tableViewData->selectionModel();
	assert(sel);
	connect(sel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(on_tableViewDataSelModel_selectionChanged(QItemSelection, QItemSelection)));

	connect(this, SIGNAL(viewMode_Changed(ViewMode)), this, SLOT(on_ViewModeChanged(ViewMode)));
	connect(this, SIGNAL(viewMode_Changed(ViewMode)), ui.tableViewData, SLOT(on_ViewModeChanged(ViewMode)));
	connect(this, SIGNAL(viewMode_Changed(ViewMode)), &_model, SLOT(reset(ViewMode)));
	connect(this, SIGNAL(viewMode_Changed(ViewMode)), &_byteDelegate, SLOT(setViewMode(ViewMode)));

	emit viewMode_Changed(vmDecimal);

	//_model.setLoadFilter(&_ss7filter);

	readSettings();

	recreateCRCLabels();

	if (QCoreApplication::arguments().count() >= 2){
		openFile(QCoreApplication::arguments().at(1));
	}
}

DataAnalyzer::~DataAnalyzer()
{
	writeSettings();
}

void DataAnalyzer::on_actionOpen_triggered()
{
        QPointer<OpenFileDialog> fd = new OpenFileDialog(this);

        if (!fd->exec())
		return;

        QString fn = fd->selectedFiles().at(0);
        openFile(fn, fd->getSplitSize());

}

void DataAnalyzer::on_actionCRCconfig_triggered()
{
	CrcConfigDlg dlg(_crc_infos, this);
	dlg.exec();
	recreateCRCLabels();
}

//void DataAnalyzer::on_comboboxViewMode_currentIndexChanged( int index )
//{
//	ViewMode vm;
//	switch (index)
//	{
//	case 0: vm = vmDecimal; break;
//	case 1: vm = vmHEX; break;
//	case 2: vm = vmBinary; break;
//	case 3: vm = vmBinaryInverted; break;
//	case 4: vm = vmSymbol; break;
//	}
//	emit viewMode_Changed(vm);
//}

void DataAnalyzer::selectionChanged( const QItemSelection & selected, const QItemSelection & deselected )
{
	on_tableViewDataSelModel_selectionChanged(selected, deselected);
}

void DataAnalyzer::on_tableViewDataSelModel_selectionChanged( const QItemSelection & selected, const QItemSelection & deselected )
{
	QModelIndexList indexes = ui.tableViewData->selectionModel()->selectedIndexes();

	QByteArray ba;

	int size = indexes.size();

	for (int i = 0, end = size; i < end; ++i) {
		ba.append( (char)indexes.at(i).data(Qt::DisplayRole).toInt() );
	}	

	ui.labelSelectedBlockSize->setText(QString::number(size));

	char * buffer = ba.data();

	for(CRC_CONTAINER::const_iterator itr = _crc_infos.begin(), end = _crc_infos.end(); itr != end; ++itr)
	{
		QString crc = "";
		if (itr->bitsize == crc_info::bits16)
		{
			boost::crc_basic<16>  crc_ccitt1( 
				itr->crc16.truncated_polynominal, 
				itr->crc16.initial_remainder, 
				itr->crc16.final_xor_value, 
				itr->reflect_input,
				itr->reflect_remainder);
			crc_ccitt1.process_bytes( buffer, size );
			unsigned short bcrc = crc_ccitt1.checksum();
			crc = QString::number(bcrc, 16).toUpper();
		}
		else if (itr->bitsize == crc_info::bits32)
		{
			boost::crc_basic<32>  crc_ccitt1( 
				itr->crc32.truncated_polynominal, 
				itr->crc32.initial_remainder, 
				itr->crc32.final_xor_value, 
				itr->reflect_input,
				itr->reflect_remainder);
			crc_ccitt1.process_bytes( buffer, size );
			unsigned long bcrc = crc_ccitt1.checksum();
			crc = QString::number(bcrc, 16).toUpper();
		}
		_CRC_dynamic_labels[itr.key()].second.data()->setText(crc);
	}

	//unsigned int crc16 = qChecksum(ba.data(), ba.size());
}

void DataAnalyzer::openFile( const QString & filename, int splitsize /*= -1*/ )
{
	if (!QFile::exists(filename)) return;
	_model.load(filename, splitsize);
}

void DataAnalyzer::writeSettings()
{
	QSettings settings(INI_FILENAME, QSettings::IniFormat);
	settings.beginGroup(SETIINGS_CRC_GROUP);
	settings.setValue("CRC_Count", _crc_infos.size());
	int i = 0;
	for (CRC_CONTAINER::const_iterator itr = _crc_infos.begin(), end = _crc_infos.end(); itr != end; ++itr, ++i)
	{
		settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_NAME,i), itr.key());
		settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_BITS,i), itr->bitsize);
		if (itr->bitsize == crc_info::bits16)
		{
			settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_POLYNOM,i), itr->crc16.truncated_polynominal);
			settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REMINDER,i), itr->crc16.initial_remainder);
			settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_FINAL_XOR_VALUE,i), itr->crc16.final_xor_value);
		}
		else if (itr->bitsize == crc_info::bits32)
		{
                    settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_POLYNOM,i), QVariant::fromValue(itr->crc32.truncated_polynominal));
                        settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REMINDER,i), QVariant::fromValue(itr->crc32.initial_remainder));
                        settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_FINAL_XOR_VALUE,i), QVariant::fromValue(itr->crc32.final_xor_value));
		}
		settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REFLECT_INPUT,i), itr->reflect_input);
		settings.setValue(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REFLECT_REMINDER,i), itr->reflect_remainder);
	}
	settings.endGroup();
}

void DataAnalyzer::readSettings()
{
	_crc_infos.clear();

	QSettings settings(INI_FILENAME, QSettings::IniFormat);
	settings.beginGroup(SETIINGS_CRC_GROUP);

	int count = settings.value("CRC_Count", 0).toInt();
	for (int i = 0, end = count; i < end; ++i)
	{
		crc_info info;
		memset(&info, 0, sizeof(info));
		QString name = settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_NAME,i)).toString();
		info.bitsize = (crc_info::bits)settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_BITS,i)).toInt();
		if (info.bitsize == crc_info::bits16)
		{
			info.crc16.truncated_polynominal = (short)settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_POLYNOM,i), 0).toInt();
			info.crc16.initial_remainder = (short)settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REMINDER,i), 0).toInt();
			info.crc16.final_xor_value = (short)settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_FINAL_XOR_VALUE,i), 0).toInt();
		}
		else if (info.bitsize == crc_info::bits32)
		{
			info.crc32.truncated_polynominal = settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_POLYNOM,i), 0).toInt();
			info.crc32.initial_remainder = settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REMINDER,i), 0).toInt();
			info.crc32.final_xor_value = settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_FINAL_XOR_VALUE,i), 0).toInt();
		}
		info.reflect_input = settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REFLECT_INPUT,i), 0).toBool();
		info.reflect_remainder = settings.value(SETTINGS_ITERATION_NAME(SETIINGS_CRC_REFLECT_REMINDER,i), 0).toBool();
		
		_crc_infos[name] = info;
	}

	settings.endGroup();
}

void DataAnalyzer::recreateCRCLabels()
{
	_CRC_dynamic_labels.clear();

	for(CRC_CONTAINER::const_iterator itr = _crc_infos.begin(), end = _crc_infos.end(); itr != end; ++itr)
	{
		//ui.gridLayoutCRC->addItem()
		QSharedPointer<QLabel> labCrcName = QSharedPointer<QLabel>(new QLabel(ui.dockWidgetCRC));
		labCrcName->setText(itr.key());
		QSharedPointer<QLabel> labCRCValue = QSharedPointer<QLabel>(new QLabel(ui.dockWidgetCRC));
		labCRCValue->setText(0);
		ui.formLayoutCRC->addRow(labCrcName.data(), labCRCValue.data());
		_CRC_dynamic_labels[itr.key()] =  qMakePair(labCrcName, labCRCValue);
	}
}


void DataAnalyzer::on_ViewModeChanged( ViewMode vm )
{
	ui.actionViewModelDecimal->setChecked(vm==vmDecimal);
	ui.actionViewModelHEX->setChecked(vm==vmHEX);
	ui.actionViewModelBinary->setChecked(vm==vmBinary);
	ui.actionViewModelBinaryInverted->setChecked(vm==vmBinaryInverted);
	ui.actionViewModelSymbol->setChecked(vm==vmSymbol);
	ui.actionViewModelBitPixel->setChecked(vm==vmBitPixel);
	//_cmbViewMode->setCurrentIndex((int)vm);
}

void DataAnalyzer::on_actionViewModelDecimal_triggered()
{
	emit viewMode_Changed(vmDecimal);
}

void DataAnalyzer::on_actionViewModelHEX_triggered()
{
	emit viewMode_Changed(vmHEX);
}

void DataAnalyzer::on_actionViewModelBinary_triggered()
{
	emit viewMode_Changed(vmBinary);
}

void DataAnalyzer::on_actionViewModelBinaryInverted_triggered()
{
	emit viewMode_Changed(vmBinaryInverted);
}

void DataAnalyzer::on_actionViewModelSymbol_triggered()
{
	emit viewMode_Changed(vmSymbol);
}

void DataAnalyzer::on_actionViewModelBitPixel_triggered()
{
	emit viewMode_Changed(vmBitPixel);
}
